#include "stdafx.h"
#include "renderer.hpp"
#include "Object.h"
#include <LwoParser.h>
#include <boost/spirit.hpp>
#include <boost/spirit/core.hpp>
#include <boost/spirit/tree/ast.hpp>
#include <boost/spirit/iterator/file_iterator.hpp>
#include <boost/bind.hpp>


using std::vector;
using std::string;
using std::cout;
using std::auto_ptr;
using boost::spirit::grammar;
using boost::spirit::lexeme_d;
using boost::spirit::str_p;
using boost::spirit::ch_p;
using boost::spirit::alpha_p;
using boost::spirit::alnum_p;
using boost::spirit::print_p;
using boost::spirit::blank_p;
using boost::spirit::space_p;
using boost::spirit::eol_p;
using boost::spirit::anychar_p;
using boost::spirit::int_p;
using boost::spirit::real_p;
using boost::spirit::rule;
using boost::spirit::parser_context;
using boost::spirit::parser_tag;
using boost::spirit::file_iterator;
using boost::spirit::parse_info;
using boost::spirit::parse;


template <typename IteratorT>
struct Context
{
	Scene * scene;

	struct ObjectData
	{
		int layer;
		string path;
		double channels[9];

		Object * Build()
		{
			auto_ptr<Object> result(LoadLwo(path.c_str(), layer));
			result->Transform(Matrix4(
				Vector3  (channels[0], channels[1], channels[2]),   // pos
				Rotations(channels[3], channels[4], channels[5]),   // rotations
				Scales   (channels[6], channels[7], channels[8]))); // scales
			return result.release();
		}
	} objectData;

	double (* channels)[9];
	int nextChanId;

	void putObjectLayer(int layer)
	{
		objectData.layer = layer;
		channels = &objectData.channels;
	}

	void putObjectPath(IteratorT first, IteratorT end)
	{
		objectData.path = string(first, end);
	}

	void finishObject()
	{
		scene->addObject(objectData.Build());
	}

	void putChannelId(int channelId)
	{
		nextChanId = channelId;
	}

	void putChannelValue(double value)
	{
		(*channels)[nextChanId] = value;
	}
};


struct LwsGrammar : public grammar<LwsGrammar>
{
	auto_ptr<Scene> scene;

	template <typename ScannerT>
	struct definition
	{
		typedef Context<typename ScannerT::iterator_t> context_t;
		context_t ctx;

		definition(LwsGrammar const & self)
		{
			ctx.scene = self.scene.get();

			/* Grammar */
			startPoint =
				"LWSC" >>
				int_p >> // version
				*(skipPlugin | sequenceOrBlock);

			skipPlugin = "Plugin" >> lexeme_d[+(anychar_p - "EndPlugin")] >> "EndPlugin";

			// nodes alternatives, from most concrete to generic
			sequenceOrBlock = loadObjectSequence | addLightSequence |
				addCameraSequence | skipMultiOrSingleLineBlock;

			skipMultiOrSingleLineBlock = skipMultiLineBlock | skipSingleLineBlock;
			skipSingleLineBlock = ~ch_p('{') >> lexeme_d[+(anychar_p - eol_p)];
			skipMultiLineBlock =
				"{" >> identifier >>
				lexeme_d[+(anychar_p - eol_p)] >> // skip data
				+skipMultiOrSingleLineBlock >> // skip sub nodes
				"}";

			//OneParDelegate beginLoadObjectLayerFunctor(ctx);
			loadObjectSequence =
				("LoadObjectLayer" >> int_p[bind(&context_t::putObjectLayer, &ctx, _1)] >> path[bind(&context_t::putObjectPath, &ctx, _1, _2)] >>
				"ShowObject" >> int_p >> int_p >> // skip ShowObject
				objectMotion)[bind(&context_t::finishObject, &ctx)];

			objectMotion =
				"ObjectMotion" >>
				channels;

			addLightSequence =
				"AddLight" >>
				skipSingleLineBlock >> // skip LightName
				skipSingleLineBlock >> // skip ShowLight
				skipSingleLineBlock >> // skip LightMotion
				channels >>
				"LightColor" >> real_p >> real_p >> real_p >>
				"LightIntensity" >> real_p >>
				!("AffectCaustics" >> lexeme_d[+(anychar_p - eol_p)]) >>
				!("LightType" >> lexeme_d[+(anychar_p - eol_p)]) >>
				"LightQuality" >> int_p >>
				skipSingleLineBlock >> // skip ShadowType
				skipSingleLineBlock; // skip ShadowColor

			addCameraSequence =
				str_p("AddCamera") >>
				"CameraName" >> lexeme_d[+(anychar_p - eol_p)] >>
				"ShowCamera" >> lexeme_d[+(anychar_p - eol_p)] >>
				cameraMotion >>
				"ZoomFactor" >> lexeme_d[+(anychar_p - eol_p)] >>
				"ResolutionMultiplier" >> lexeme_d[+(anychar_p - eol_p)] >>
				"FrameSize" >> int_p >> int_p;

			cameraMotion =
				"CameraMotion" >>
				channels;

			channels = "NumChannels" >> int_p >>
				*(channel);

			channel = "Channel" >> int_p[bind(&context_t::putChannelId, &ctx, _1)] >> envilopeBlock;

			envilopeBlock =
				ch_p('{') >> "Envelope" >>
				keys >>
				"Behaviors" >> int_p >> int_p >> // skip Behaviors
				'}';

			keys = int_p >> // num keys
				*keyBlock;

			// concrete rules
			keyBlock = "Key" >> real_p[bind(&context_t::putChannelValue, &ctx, _1)] >> lexeme_d[*(anychar_p - eol_p)];

			path = lexeme_d[
				!(!(alpha_p >> ':') >> (ch_p('\\') | '/')) >> // c:\ part
				*(print_p - '\\' - '/' - ':' - '*' - '?' - '"' - '|') >>
				*((ch_p('\\') | '/') >>
				*(print_p - '\\' - '/' - ':' - '*' - '?' - '"' - '|'))];

			identifier = lexeme_d[alpha_p >> *alnum_p];

			BOOST_SPIRIT_DEBUG_RULE(startPoint);
			BOOST_SPIRIT_DEBUG_RULE(skipPlugin);
			BOOST_SPIRIT_DEBUG_RULE(sequenceOrBlock);
			BOOST_SPIRIT_DEBUG_RULE(addCameraSequence);
			BOOST_SPIRIT_DEBUG_RULE(cameraMotion);
			BOOST_SPIRIT_DEBUG_RULE(loadObjectSequence);
			BOOST_SPIRIT_DEBUG_RULE(objectMotion);
			BOOST_SPIRIT_DEBUG_RULE(addLightSequence);
			BOOST_SPIRIT_DEBUG_RULE(channel);
			BOOST_SPIRIT_DEBUG_RULE(channels);
			BOOST_SPIRIT_DEBUG_RULE(envilopeBlock);
			BOOST_SPIRIT_DEBUG_RULE(keys);
			BOOST_SPIRIT_DEBUG_RULE(keyBlock);
			BOOST_SPIRIT_DEBUG_RULE(skipMultiOrSingleLineBlock);
			BOOST_SPIRIT_DEBUG_RULE(skipSingleLineBlock);
			BOOST_SPIRIT_DEBUG_RULE(skipMultiLineBlock);
			BOOST_SPIRIT_DEBUG_RULE(path);
			BOOST_SPIRIT_DEBUG_RULE(identifier);
			BOOST_SPIRIT_DEBUG_RULE(path);
			BOOST_SPIRIT_DEBUG_RULE(identifier);
		}

		rule<ScannerT> startPoint;
		rule<ScannerT> skipPlugin;
		rule<ScannerT> sequenceOrBlock;
		rule<ScannerT> skipMultiOrSingleLineBlock;
		rule<ScannerT> skipMultiLineBlock;
		rule<ScannerT> skipSingleLineBlock;
		rule<ScannerT> addCameraSequence;
		rule<ScannerT> cameraMotion;
		rule<ScannerT> loadObjectSequence;
		rule<ScannerT> objectMotion;
		rule<ScannerT> addLightSequence;
		rule<ScannerT> channel;
		rule<ScannerT> channels;
		rule<ScannerT> envilopeBlock;
		rule<ScannerT> keys;
		rule<ScannerT> keyBlock;
		rule<ScannerT> path;
		rule<ScannerT> identifier;

		rule<ScannerT> const& start() const { return startPoint; }
	};

	template <typename NodeT>
	void DispatchObjectSequenceInternals(NodeT const & node)
	{
		if (node.value.id() == ObjectMotionID)
		{
			long numChanels = strtol(
				string(node.children[0].value.begin(), node.children[0].value.end()).c_str(), 0, 10);
		}
	}

	template <typename ContainerT>
	void TraverseObjectSequence(ContainerT const & items)
	{
		long layer = strtol(string(items[0].value.begin(), items[0].value.end()).c_str(), 0, 10);
		string path(items[1].value.begin(), items[1].value.end());
		Object = LoadLwo(path.c_str());
		for (size_t i = 2; i != items.size(); i++)
		{
			DispatchObjectSequenceInternals(items[i]);
		}
	}

	template <typename IteratorT>
	void SequencesDispatch(IteratorT const & iter)
	{
		if (iter->value.id() == LoadObjectSequenceID)
		{
			TraverseObjectSequence(iter->children);
		}
	}
};


Scene * LoadLws(const char * fileName)
{
	// setup file iterator
	file_iterator<> first(fileName);
	if (!first)
	{
		throw runtime_error(string("Unable to open file ") + fileName);
	}
	file_iterator<> last = first.make_end();

	// parse
	LwsGrammar lwsGrammar;
	parse_info<file_iterator<> > info = parse(first, last, lwsGrammar, space_p);

	// fill scene from abstract syntax tree
	Scene * scene = new Scene();
	/*lwsGrammar.Scene = scene;
	lwsGrammar.SequencesDispatch(info.trees.begin());*/
	return scene;
}
